﻿using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using Singer.Core;
using Singer.Shared;
using System.Text;

namespace Singer.Middleware.JwtAuth
{
    public static class JwtAuthServiceExtensions
    {
        public static void AddJwtAuth(this IServiceCollection services, IConfiguration configuration)
        {
            var config = configuration.GetSection(JwtOptions.ConfigKey);
            JwtOptions? options = config.Get<JwtOptions>();
            if (options == null)
                throw new Exception("【JwtAuth】Config was not found.");
            options.Validate();
            services.Configure<JwtOptions>(config);
            services.AddJwtAuthentication(configuration);//Jwt鉴权
            services.AddSingleton<IJwtAuthTokenManager, JwtAuthTokenManager>();//Jwt Token Provider
            services.AddMvc(opt =>
            {
                opt.Filters.Add<JwtAuthCheckFilter>();
            });
        }

        /// <summary>
        /// jwt 鉴权
        /// </summary>
        private static void AddJwtAuthentication(this IServiceCollection services, IConfiguration configuration)
        {
            JwtOptions? options = configuration.GetSection(JwtOptions.ConfigKey).Get<JwtOptions>();
            if (options == null)
                throw new Exception("【JwtAuth】Config was not found.");
            options.Validate();
            SecurityKey securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(options.SecurityKey));
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(opt =>
                {
                    //jwt验证参数设置
                    opt.TokenValidationParameters = new TokenValidationParameters
                    {
                        //JWT有一些默认的属性，就是给鉴权时就可以筛选了
                        ValidateIssuer = true,//是否验证Issuer--如果要验证---必须让token中的Issuer和这里配置的一样
                        ValidateAudience = true,//是否验证Audience--如果要验证---必须让token中的Audience和这里配置的一样
                        ValidateLifetime = true,//是否验证失效时间
                        ValidateIssuerSigningKey = true,//是否验证SecurityKey
                        ValidAudience = options.Audience,
                        ValidIssuer = options.Issuer,//Issuer，这两项和前面签发jwt的设置一致
                        IssuerSigningKey = securityKey,
                    };
                    opt.SaveToken = true;//每次请求时都保存token到HttpContext中
                    //jwt验证不通过，触发的事件
                    opt.Events = new JwtBearerEvents
                    {
                        //此处为权限验证失败后触发的事件
                        OnChallenge = async context =>
                        {
                            //此处代码为终止.Net Core默认的返回类型和数据结果，这个很重要哦，必须
                            context.HandleResponse();
                            //自定义返回的数据类型
                            context.Response.ContentType = "application/json";
                            //自定义返回状态码，默认为401 我这里改成 200
                            context.Response.StatusCode = StatusCodes.Status200OK;
                            await context.Response.WriteAsync(JsonUtils.ToJson(new CoreApiResult("auth-error", "身份认证失效")));
                        }
                    };
                });
        }
    }
}
